home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Freeware / Miro 1.0 / Miro_Installer.exe / Miro_Downloader.exe / templateoptimize.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2007-11-12  |  7.7 KB  |  224 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.5)
  3.  
  4. import re
  5. import logging
  6. from xml.sax.expatreader import ExpatParser
  7. from xml.sax.handler import ContentHandler
  8. hotspotMarkerPattern = re.compile('<!-- HOT SPOT ([^ ]*) -->')
  9.  
  10. class ChangeItemHint:
  11.     '''Contains hints the frontend can use to optimize
  12.     HTMLDisplay.changeItem().  Member attributes:
  13.  
  14.  
  15.     changedInnerHTML -- The inner HTML of the element being changed.  If
  16.         nothing changed inside the element this will be None.
  17.     changedAttributes -- A dict containing the changes to the DOM element
  18.         attributes.  Each attribute that changed will have an entry.  The key
  19.         is the attribute name.  If the attribute was removed, the value will
  20.         be None.
  21.     '''
  22.     
  23.     def __init__(self, changedAttributes, changedInnerHTML):
  24.         self.changedAttributes = changedAttributes
  25.         self.changedInnerHTML = changedInnerHTML
  26.  
  27.  
  28.  
  29. class SingleElementHandler(ContentHandler):
  30.     '''XML Content handler that just reads in the first elements tagname and
  31.     attributes.
  32.     '''
  33.     
  34.     def __init__(self):
  35.         self.started = False
  36.         ContentHandler.__init__(self)
  37.  
  38.     
  39.     def startElement(self, name, attrs):
  40.         self.name = name
  41.         self.attrs = attrs
  42.         self.started = True
  43.  
  44.     
  45.     def reset(self):
  46.         self.started = False
  47.  
  48.  
  49.  
  50. class BrokenUpElement:
  51.     """Breaks up an HTML element into it's outermost tag and inner html.
  52.     
  53.     Attributes:
  54.       html -- original HTML
  55.       name -- outermost tag name
  56.       attrs -- outermost tag attributes
  57.       innerHTML -- data inside the outermost tag.
  58.  
  59.     """
  60.     parser = ExpatParser()
  61.     handler = SingleElementHandler()
  62.     parser.setContentHandler(handler)
  63.     
  64.     def __init__(self, id, html):
  65.         self.id = id
  66.         self.html = html
  67.         innerHTMLStart = self.feedInFirstElement(html)
  68.         innerHTMLEnd = html.rfind('</')
  69.         self.innerHTML = html[innerHTMLStart:innerHTMLEnd]
  70.         self.name = self.handler.name
  71.         self.attrs = self.handler.attrs
  72.         self.handler.reset()
  73.         self.parser.reset()
  74.  
  75.     
  76.     def feedInFirstElement(self, html):
  77.         pos = 0
  78.         while not self.handler.started:
  79.             end_candidate = html.find('>', pos)
  80.             if end_candidate < 0:
  81.                 raise ValueError("Can't find start tag in %s" % html)
  82.             
  83.             self.parser.feed(html[pos:end_candidate + 1])
  84.             pos = end_candidate + 1
  85.         return pos
  86.  
  87.     
  88.     def calcChanges(self, older):
  89.         '''Calculate the changes between self and an older version of this
  90.         element.  Return a list of changes to pass to HTMLArea.changeItems.
  91.         '''
  92.         attrDiff = self.calcAttributeChanges(older.attrs, self.attrs)
  93.         newInnerHTML = self.calcNewInnerHTML(older.innerHTML, self.innerHTML)
  94.         if attrDiff or newInnerHTML:
  95.             hint = ChangeItemHint(attrDiff, newInnerHTML)
  96.             return [
  97.                 (self.id, self.html, hint)]
  98.         else:
  99.             return []
  100.  
  101.     
  102.     def calcNewInnerHTML(self, oldInnerHTML, newInnerHTML):
  103.         '''Calculate the newInnerHTML argument to pass to HTMLArea.changeItem.
  104.         '''
  105.         if oldInnerHTML != newInnerHTML:
  106.             return newInnerHTML
  107.         else:
  108.             return None
  109.  
  110.     
  111.     def calcAttributeChanges(self, oldAttrs, newAttrs):
  112.         '''Calculate the difference between two attribute dicts.  Returns a dict
  113.         with entries for each key that has changed.  Keys that have been removed
  114.         will have None values.
  115.         '''
  116.         changes = dict(newAttrs)
  117.         for key, value in oldAttrs.items():
  118.             
  119.             try:
  120.                 if changes[key] == value:
  121.                     del changes[key]
  122.             continue
  123.             except KeyError:
  124.                 changes[key] = None
  125.                 continue
  126.             
  127.  
  128.         
  129.         return changes
  130.  
  131.  
  132.  
  133. class OptimizedElement:
  134.     '''Used by HTMLChangeOptimizer to calculate how an html element changed.'''
  135.     
  136.     def __init__(self, id, html):
  137.         self.brokenUp = BrokenUpElement(id, html)
  138.         self.calcHotSpots()
  139.  
  140.     
  141.     def calcHotSpots(self):
  142.         self.hotspots = { }
  143.         split = hotspotMarkerPattern.split(self.brokenUp.html)
  144.         self.outerParts = [
  145.             split[0]]
  146.         i = 1
  147.         while i < len(split):
  148.             (hotspotID, hotspotHTML, end, outerPart) = split[i:i + 4]
  149.             self.hotspots[hotspotID] = BrokenUpElement(hotspotID, hotspotHTML)
  150.             self.outerParts.append(hotspotID)
  151.             self.outerParts.append(outerPart)
  152.             i += 4
  153.  
  154.     
  155.     def calcChanges(self, older):
  156.         if len(self.outerParts) == 1 or self.outerParts != older.outerParts:
  157.             return self.brokenUp.calcChanges(older.brokenUp)
  158.         else:
  159.             changes = []
  160.             for id in self.hotspots:
  161.                 new = self.hotspots[id].calcChanges(older.hotspots[id])
  162.                 changes.extend(new)
  163.             
  164.             return changes
  165.  
  166.  
  167.  
  168. class HTMLChangeOptimizer:
  169.     '''Class that handles changing xml in an efficient way.  It currently
  170.     optimizes a few cases:
  171.  
  172.       * If the html stays the same, we don\'t send anything to the frontend
  173.         code.
  174.       * We calculate a ChangeItemHint for each element that changes.  Smart
  175.         frontends can use this to optimize the changeItem() call, especially
  176.         when only the change is repeated element\'s attributes.
  177.       * Child elements can be marked "hotspots" meaning they are more likely 
  178.         change than other parts.  We only update the hotspot elements when we
  179.         detect that other html hasn\'t changed.
  180.  
  181.     We mark a region as a hotspot by adding specially formatted comments like
  182.     so:
  183.  
  184.     <!-- HOT SPOT hotspot-dom-id --><div id="hotspot-dom-id">
  185.        blah blah blah
  186.     </div><!-- HOT SPOT END -->
  187.     '''
  188.     
  189.     def __init__(self):
  190.         self.elements = { }
  191.  
  192.     
  193.     def setInitialHTML(self, id, html):
  194.         '''Set the initial HTML for a dom element.  This must be called before
  195.         calcChanges() is called with id
  196.         '''
  197.         self.elements[id] = OptimizedElement(id, html)
  198.  
  199.     
  200.     def calcChanges(self, id, html):
  201.         '''Calculate a list of arguments to pass to HTMLArea.changeItems().'''
  202.         
  203.         try:
  204.             old = self.elements[id]
  205.         except KeyError:
  206.             logging.warn('KeyError for element %s in HTMLChangeOptimizer.calcChanges()', id)
  207.             return [
  208.                 (id, html, None)]
  209.  
  210.         new = OptimizedElement(id, html)
  211.         self.elements[id] = new
  212.         return new.calcChanges(old)
  213.  
  214.     
  215.     def removeElements(self, ids):
  216.         for id in ids:
  217.             if id in self.elements:
  218.                 del self.elements[id]
  219.                 continue
  220.             logging.warn('Trying to remove an unknown element.')
  221.         
  222.  
  223.  
  224.